home *** CD-ROM | disk | FTP | other *** search
- Subject: v06i101: Count unread news articles (newscnt)
- Newsgroups: mod.sources
- Approved: rs@mirror.UUCP
-
- Submitted by: ihnp4!inuxd!gat
- Mod.sources: Volume 6, Issue 101
- Archive-name: newscnt
-
- This program has been used here for several months and seems to be
- stable. I haven't had the opportunity to try it out on other UNIXes.
- Glen A. Taylor
- AT&T (Consumer Products Division)
- Indianapolis, IN (317) 845-3709
- ihnp4!inuxd!gat
-
- [ With Glen's permission, I added the code to use getopt rather than
- scanargs, and the "#ifdef BSD" code; I don't think I broke anything.
- Note that this program uses strtok() -- if it's not in your library,
- the mod.sources archives have two public-domain implementations of
- it and the other string functions. I haven't tested the program as
- extensively as I might like to, because its utility to me is limited;
- we use notes here. --r$ ]
-
- #!/bin/sh
- # This is a shell archive. Remove anything before this line,
- # then unpack it by saving it in a file and typing "sh file".
- # Wrapped by rs@mirror.UUCP on Sun Aug 3 19:56:49 EDT 1986
- # Contents: README Makefile newscnt.1 newscnt.c scanargs.c
-
- echo x - README
- sed 's/^XX//' > "README" <<'@//E*O*F README//'
- XXHere is a little utility that I hope others will find as useful as I
- XXhave. Ever since I started to read the net, I have wanted a means to
- XXdetermine how much news is waiting for me before I dive into it. When
- XXthe "wn" program came across net.sources a couple months ago, I thought
- XXmy problem was solved. Unfortunately, that program didn't do what I
- XXwanted; I am not interested in knowing all of the unread news on the
- XXsystem but only the news groups that I read. Consequently I decided to
- XXwrite "newscnt."
-
- XXNewscnt makes use of your .newsrc file and the system's
- XX/usr/lib/news/active file to determine your unread news in much the
- XXsame way that readnews or vnews does. It looks at your "option" line
- XXin .newsrc to determine the news groups that interest you and then
- XXbuild the structures it needs to count your unread articles. By
- XXdefault, newscnt only returns a total, but with the "-a" option it will
- XXgive you a breakdown by newsgroup. A manual page is included that
- XXdescribes all the bells and whistles.
-
- XXNewscnt uses the "scanargs" argument parser that was distributed quite
- XXsome time ago on this net. I have included a copy in this distribution
- XXfor those who don't have one.
-
- XXAny bug reports, suggestions for improvement, other complaints or even
- XXpraise should be sent to:
- XX Glen A. Taylor
- XX ihnp4!inuxd!gat
- XX AT&T (Consumer Products Division)
- XX P.O. Box 1008
- XX Indianapolis, IN 46206 (317) 845-3709
- @//E*O*F README//
- chmod u=rw,g=rw,o=rw README
-
- echo x - Makefile
- sed 's/^XX//' > "Makefile" <<'@//E*O*F Makefile//'
- XX## If on a BSD system, enable the next line. Also, make arrangements to
- XX## get strtok(3) pulled in somehow.
- XX#BSD = -DBSD
-
- XX## If you want getopt, enable this line:
- XXGETOPT = -DUSE_GETOPT
- XX## You may have to have something like this line, if getopt isn't in libc.a
- XX#GETOBJ = -lgetopt
- XX## If no getopt, enable this line:
- XX#SCAN = scanargs.o
-
- XXCFLAGS=$(BSD) $(GETOPT)
-
- XXnewscnt: newscnt.o $(SCAN)
- XX cc -o newscnt newscnt.o $(SCAN) $(GETOBJ)
- @//E*O*F Makefile//
- chmod u=rw,g=rw,o=rw Makefile
-
- echo x - newscnt.1
- sed 's/^XX//' > "newscnt.1" <<'@//E*O*F newscnt.1//'
- XX.TH NEWSCNT 1 LOCAL
- XX.SH NAME
- XXnewscnt \- count unread network news based on user's newsgroup
- XXchoices
- XX.SH SYNOPSIS
- XX.B newscnt
- XX[
- XX.BR - { sta }
- XX] [
- XX.B -x
- XX] [
- XX.B -f
- XXalt_.newsrc ] [
- XX.B -n
- XXnewsgrp ...]
- XX.SH DESCRIPTION
- XX.I Newscnt
- XXprovides a count of unread network news. By default, the user's .newsrc
- XXfile is examined to determine a list of newsgroups. These newsgroups
- XXare then matched against the list of articles that the user has read
- XXand a count of available unread news articles is determined. The
- XXprogram writes to stdout either "No news." if there are no unread news
- XXarticles, or "\f2nnn\fP news articles." to indicate the number of
- XXunread articles. Options
- XXare provided for altering the format in which the count(s) are
- XXreturned and the inputs the program works from.
- XX.P
- XXThe following options are supported:
- XX.TP .5i
- XX\f3-\fP{\f3sta\fP}
- XXThese three options, only one of which may be specified at a time,
- XXaffect the output format of the program.
- XX.RS .5i
- XX.TP .5i
- XX\f3s\fP
- XXThe \f2silent\fP option -- nothing is written to stdout. Only the
- XXprogram's normal return codes are provided, e.g. a \f31\fP if there is
- XXno unread news, a \f30\fP if there is unread news, or a \f3-1\fP if
- XXthere was an error in the processing.
- XX.TP .5i
- XX\f3t\fP
- XXThe \f2terse\fP option -- only the total number of unread news
- XXarticles (including zero if there are no unread news articles) is
- XXwritten to stdout. No other text is written.
- XX.TP .5i
- XX\f3a\fP
- XXThe \f2all\fP option -- in addition to a report of the total number of
- XXunread news articles, the program writes a line of the form
- XX"\f2newsgroup\fP: \f2nnn\fP articles" for each newsgroup having one or
- XXmore unread news articles.
- XX.RE
- XX.TP .5i
- XX\f3-x\fP
- XXThis flag causes the program to ignore the articles that the user has
- XXalready read in each newsgroup and report the total number of articles
- XXavailable in the newsgroup. The effect is similar to the \f3-x\fP
- XXflag on most news reading programs.
- XX.TP .5i
- XX\f3-f \f2alt_.newsrc\fP
- XXWhen this option is specified, the file \f2alt_.newsrc\fP is used
- XXinstead of the user's \f2$HOME/.newsrc\fP file.
- XX.TP .5i
- XX\f3-n \f2newsgrps\fP ...
- XXWhen this option is specified, the \f2newsgrps\fP specified on the
- XXcommand line are reported on. By default, the program searches
- XXthrough the specified or default .newsrc file for a line that begins
- XXwith the word \f2options\fP. This \f2options\fP line is parsed for the
- XXlist of \f2newsgrps\fP following a \f3-n\fP argument. Any
- XX\f2newsgrp\fP, either from the command line or from the .newsrc file,
- XXprefaced with an exclamation point will be specifically excluded from
- XXthe computation. This is useful for excluding "embedded" newsgroups.
- XX(\f3NOTE\fP: A \f2newsgrp\fP is taken as the "prefix" to a set of
- XXnewsgroups and all newsgroups with that prefix are automatically
- XXincluded in the count.)
- XX.SH FILES
- XX/usr/lib/news/active
- XX.br
- XX$HOME/.newsrc
- XX.SH CAVEATS
- XXThis program makes use of the /usr/lib/news/active file to determine
- XXthe available news items and not the actual news directory's contents.
- XXConsequently, its answers may be off slightly if the active file does not
- XXaccurately track the available news articles. If you are told
- XXyou have unusually large amounts of unread news, use the -a option to
- XXpinpoint the newsgroup(s) with unusual counts
- XXand then check to see if there are unexpired
- XXarticles causing an unusually large "range" in the active file.
- XX.P
- XXThe .newsrc format is interpreted according to the author's understanding of
- XXthat file's format. Variations in how this file is interpreted may exist.
- XX.SH AUTHOR
- XX.nf
- XXGlen A. Taylor
- XXAT&T (Consumer Products Divsion)
- XXMay 1986
- XX.fi
- @//E*O*F newscnt.1//
- chmod u=rw,g=rw,o=rw newscnt.1
-
- echo x - newscnt.c
- sed 's/^XX//' > "newscnt.c" <<'@//E*O*F newscnt.c//'
- XX/* newscnt -- a program to enumerate unread net.news articles *
- XX * *
- XX * Glen A. Taylor (AT&T-IS) May 1986 *
- XX * *
- XX * Added the code inside the BSD and USE_GETOPT #ifdef's *
- XX * Rich $alz (Mirror Systems) June 1986 *
- XX * */
-
-
- XXstatic char sccsid[] = "@(#)newscnt.c 1.2";
-
- XX#define BSD
- XX#define DEBUG
-
- XX#include <stdio.h>
- XX#ifdef BSD
- XX#include <strings.h>
- XXchar *strtok();
- XX#else
- XX#include <string.h>
- XX#endif
- XX#include <pwd.h>
- XX#include <ctype.h>
-
- XX#define FALSE 0
- XX#define TRUE 1
- XX#define SILENT 4
- XX#define TERSE 2
- XX#define ALL 1
- XX#define ERROR -1
- XX#define NEWS 0
- XX#define NONEWS 1
- XX#define MAXLENG 512
- XX#define MAXGRPS 128
- XX#define ACTIVE "/usr/lib/news/active"
- XX#define NEWSRC ".newsrc"
- XX#ifdef BSD
- XX#define USER_NAME "USER"
- XX#else
- XX#define USER_NAME "LOGNAME"
- XX#endif
-
- XXstruct grp {
- XX char *name; /* news group name */
- XX long lowest; /* lowest article number */
- XX long highest; /* highest article number */
- XX int done; /* to indicate this element has been processed */
- XX struct grp *ptr; /* ptr. to next element */
- XX};
-
- XXtypedef struct grp *GRPTR;
-
- XXchar *namevec[MAXGRPS]; /* array of newsgroup name strings */
-
- XXmain(argc,argv)
- XXint argc;
- XXchar **argv;
- XX{
-
- XX char *alt_newsrc, **newsgrps, *malloc();
- XX int silent, terse, all, flags, fflag, nflag, xflag, nr_newsgrps;
- XX FILE *fpnewsrc, *fpactive;
- XX int count = 0;
- XX GRPTR listhead = NULL;
-
- XX /* Parse arguments and establish working parameters */
- XX#ifdef USE_GETOPT
- XX {
- XX char c;
- XX extern char *optarg;
- XX extern int optind;
-
- XX nr_newsgrps = silent = terse = all = xflag = fflag = nflag = flags = 0;
- XX while ((c = getopt(argc, argv, "staxf:n:")) != EOF)
- XX switch (c) {
- XX default:
- XXUsage:
- XX fprintf(stderr,
- XX "usage: %s [-{sta}] [-x] [-f alt_.newsrc] -n newsgrp...",
- XX argv[0]);
- XX exit(1);
- XX case 's': silent = 1; break;
- XX case 't': terse = 1; break;
- XX case 'a': all = 1; break;
- XX case 'x': xflag = 1; break;
- XX case 'f': fflag = 1; alt_newsrc = optarg; break;
- XX case 'n': nflag = 1; nr_newsgrps += addgrp(optarg); break;
- XX }
- XX if (silent + terse + all > 1)
- XX goto Usage;
- XX for (argv += optind; *argv; argv++)
- XX nr_newsgrps += addgrp(argv);
- XX }
- XX#else /* !USE_GETOPT */
- XX if (!scanargs(argc,argv, "% sta%- x%- f%-alt_.newsrc!s n%-newsgrp!*s",
- XX &flags, &xflag, &fflag, &alt_newsrc, &nflag, &nr_newsgrps, &newsgrps))
- XX exit(1);
- XX silent = flags & SILENT;
- XX terse = flags & TERSE;
- XX all = flags & ALL;
- XX#endif /* USE_GETOPT */
-
- XX /* Open the .newsrc file */
- XX if (fflag) { /* use the file name the user supplied */
- XX fpnewsrc = fopen(alt_newsrc,"r");
- XX }
- XX else { /* determine the user's .newsrc file and open */
- XX {
- XX char *fname[MAXLENG];
- XX char *uname, *getenv();
- XX struct passwd *passptr, *getpwnam();
-
- XX uname = getenv(USER_NAME); /* get user's login name */
- XX passptr = getpwnam(uname); /* get user's home dir */
- XX /* construct .newsrc path */
- XX strcpy(fname, passptr -> pw_dir);
- XX strcat(fname, "/");
- XX strcat(fname, NEWSRC);
-
- XX fpnewsrc = fopen(fname,"r");
- XX }
- XX }
-
- XX /* Derive array of newsgroup names to search for */
- XX if (!nflag) getgrps(fpnewsrc, &nr_newsgrps, &newsgrps);
-
- XX#ifdef DEBUG
- XX { int i;
- XX printf("Number of newsgroups = %d\n", nr_newsgrps);
- XX for (i = 0; i < nr_newsgrps; i++) printf("%s\n", newsgrps[i]);
- XX }
- XX#endif
-
- XX if (nr_newsgrps == 0) exit (ERROR); /* can't do anything! */
-
- XX /* Open the ACTIVE newsgroup file */
- XX if ((fpactive = fopen(ACTIVE, "r"))==NULL)
- XX exit(ERROR); /* cannot proceed w/o ACTIVE file */
-
- XX /* Go through the ACTIVE file and make linked list of all
- XX newsgroups matching elements of the "newsgrps" array */
- XX {
- XX char gname[MAXLENG]; /* tmp to hold active group name */
- XX long low, high; /* tmps to hold active group low & high numbers */
- XX int i, j;
- XX char *sp, junk[10];
- XX GRPTR current, next;
-
- XX while (fscanf(fpactive,"%s %ld %ld %s\n", gname, &high, &low, junk)
- XX != EOF) { /* read the entire ACTIVE file */
-
- XX /* Compare gname to every name in newsgrps */
- XX for (i=0; i < nr_newsgrps; i++) {
- XX sp = newsgrps[i];
- XX j = strlen(sp);
- XX /* check for a group to be explicitly skipped */
- XX if (*sp == '!') {
- XX sp++;
- XX if (strncmp(gname,sp,j-1) == 0) break;
- XX }
- XX /* otherwise check for a positive match */
- XX else if (strncmp(gname,sp,j) == 0) {
- XX /* got a matching group so allocate new list element */
- XX next = (struct grp *) malloc(sizeof (struct grp));
-
- XX if (!listhead) {/* first element */
- XX listhead = next;
- XX current = next;
- XX }
- XX else {
- XX current -> ptr = next; /* link on this element */
- XX current = next;
- XX }
- XX /* fill in the list element structure */
- XX j = strlen(gname) + 1; /* get length of group name */
- XX current -> name = strcpy(malloc(j), gname);
- XX current -> lowest = low;
- XX current -> highest = high;
- XX current -> done = FALSE;
- XX current -> ptr = NULL;
-
- XX /* terminate inner loop -- proceed to next group */
- XX break;
- XX }
- XX }
- XX }
- XX }
-
- XX#ifdef DEBUG
- XX {
- XX GRPTR lptr;
- XX printf("\nList of relevant active newsgroups:\n");
- XX lptr = listhead;
- XX while (lptr != NULL) {
- XX printf("%s %d %d\n",lptr->name,lptr->highest,lptr->lowest);
- XX lptr = lptr->ptr;
- XX }
- XX }
- XX#endif
-
- XX /* Work down the linked list and compare to .newsrc entries */
- XX rewind(fpnewsrc);
- XX {
- XX GRPTR next;
- XX char line[MAXLENG], *gname, *range;
- XX int ignore, cnt;
-
- XX while(fgets(line, MAXLENG, fpnewsrc) != NULL){
- XX gname = line; /* name starts in first char position */
- XX range = line; /* find the "range" list */
- XX while (*range != ':' && *range != '!' && *range != '\0')
- XX range++; /* : and ! are the two legal name delimiters */
- XX ignore = *range == '!' ? TRUE : FALSE; /* do we ignore? */
- XX *range++ = '\0'; /* delimit gname from range */
-
- XX /* Search the list for this newsgroup */
- XX next = listhead;
- XX while (next != NULL) {
- XX if (strcmp(gname,next -> name) == 0) {/* Found it */
- XX if (!ignore) {
- XX cnt = ckrange(next, range, xflag);
- XX count = count + cnt;
- XX if (all && cnt) printf("%s: %d articles\n",gname,cnt);
- XX }
- XX next -> done = TRUE;
- XX break;
- XX }
- XX next = next -> ptr;
- XX }
- XX }
- XX /* go through list one more time and get those that aren't "done" */
- XX next = listhead;
- XX while (next != NULL) {
- XX if (! next -> done) {
- XX cnt = next -> highest - next -> lowest;
- XX if (cnt < 0) cnt = 0;
- XX count = count + cnt;
- XX /* print the nonzero entries if "all" were requested */
- XX if (all && cnt) printf("%s: %d articles\n",next->name,cnt);
- XX }
- XX next = next -> ptr;
- XX }
- XX }
-
- XX /* Print final total */
- XX if (silent) exit( count? NEWS : NONEWS);
- XX if (terse) printf("%d\n", count);
- XX else if (count) printf("%d news articles.\n",count);
- XX else printf("No news.\n");
- XX exit(count? NEWS : NONEWS);
- XX}
-
- XX#ifdef USE_GETOPT
- XX/* addgrp -- add a group list to the namevec global array.
- XX returns count of groups added. */
- XXaddgrp(p)
- XXchar *p;
- XX{
- XX static char SEPARATORS[] = " \t\n,";
- XX static int cnt;
- XX char *ptr;
- XX int i;
-
- XX ptr = strtok(p, SEPARATORS);
- XX for (i = 0; ptr = strtok(NULL, SEPARATORS); i++)
- XX namevec[cnt++] = strcpy(malloc(strlen(ptr) + 1), ptr);
- XX return(i);
- XX}
- XX#endif /* USE_GETOPT */
-
-
- XX/* getgrps -- reads the .newsrc file, finds the "option -n ... " news
- XX group list, and copies news groups into an array of
- XX strings. */
- XXgetgrps(fp, num, names)
- XXFILE *fp;
- XXint *num;
- XXchar ***names;
- XX{
- XX char line[MAXLENG], *ptr;
- XX int optflag = FALSE, nflag = FALSE;
-
- XX *names = namevec;
- XX *num = 0;
- XX while (fgets(line,MAXLENG,fp) != NULL ) {
- XX if (!optflag && strncmp(line,"option",6) == 0) {
- XX /* found an option line */
- XX optflag = TRUE;
- XX nflag = FALSE;
- XX ptr = strtok(line," \t\n");
- XX ptr = strtok(NULL," \t\n"); /* skip "option" */
- XX }
- XX else if (optflag && isspace(line[0])) {
- XX /* found a continuation line */
- XX ptr = strtok(line," \t\n");
- XX }
- XX else {
- XX optflag = FALSE;
- XX nflag = FALSE;
- XX }
-
- XX /* Parse the options line */
- XX if (optflag) {
- XX while (!nflag && ptr != NULL) {
- XX if (strcmp(ptr,"-n") == 0) {
- XX nflag = TRUE;
- XX ptr = strtok(NULL, " \t\n"); /* consume "-n" */
- XX break;
- XX }
- XX ptr = strtok(NULL, " \t\n"); /* get first news group */
- XX }
- XX while (nflag && ptr != NULL) {
- XX /* copy this news group into "newsgrps" */
- XX namevec[*num] = strcpy(malloc(strlen(ptr)+1), ptr);
- XX *num += 1;
- XX ptr = strtok(NULL, " \t\n");
- XX }
- XX }
- XX }
- XX}
-
- XX/* ckrange -- compare a range specification against a high count / low
- XX count and return the number of unread articles */
- XXint
- XXckrange(ptr, range, flag)
- XXGRPTR ptr;
- XXchar *range;
- XXint flag;
- XX{
- XX long low, high, nxthi, nxtlo;
- XX int i, extent, count;
- XX char *bitmap, *nextseg(), *rp;
-
- XX#ifdef DEBUG
- XX printf("<*> ckrange: \n\tname -- %s\n\thigh = %d\n\tlow = %d\n",
- XX ptr->name, ptr->highest, ptr->lowest);
- XX printf("\trange -- %s\n", range);
- XX#endif
-
- XX /* get low and high values for the newsgroup */
- XX low = ptr -> lowest;
- XX high = ptr -> highest;
- XX if (low > high) return (0); /* this one is screwy! */
-
-
- XX if (flag) { /* ignore range info */
- XX count = high - low;
- XX if (count < 0 ) count = 0;
- XX return (count);
- XX }
-
- XX /* set up a bitmap to do the computation */
- XX extent = high - low + 1;
- XX if (extent <= 0) return (0);
- XX bitmap = malloc(extent);
- XX if (bitmap == NULL) exit(ERROR);
- XX for (i=0; i < extent; i++) bitmap[i] = '*'; /* init. bitmap */
-
- XX /* set bits (bytes, actually) for each read article */
- XX rp = range;
- XX while ((rp = nextseg(&nxthi,&nxtlo,rp)) != NULL) {
- XX#ifdef DEBUG
- XX printf("<*> nexthi = %d, nextlo = %d\nnext seg = `%s'\n",
- XX nxthi, nxtlo, rp);
- XX#endif
-
- XX /* check that the ones read intersect the ones avail */
- XX if (nxthi < low || nxtlo > high) continue;
-
- XX /* adjust the range markers to the intersection */
- XX nxthi = (high > nxthi) ? nxthi : high;
- XX nxtlo = (low < nxtlo) ? nxtlo : low;
-
- XX /* mark the articles that were read */
- XX if (nxtlo == nxthi) bitmap[nxtlo-low] = ' ';
- XX else for (i=nxtlo-low; i <= nxthi-low; i++) bitmap[i] = ' ';
- XX }
-
- XX /* count the set bits & go home */
- XX count = 0;
- XX for (i=0; i < extent; i++) if (bitmap[i] == '*') count++;
- XX free(bitmap);
- XX return (count);
- XX}
-
- XXchar *
- XXnextseg(high, low, str)
- XXlong *high, *low;
- XXchar *str;
- XX{
- XX char *p1, *retval;
- XX long val = 0L;
- XX int hyphen = FALSE, more = TRUE;
-
- XX if (*str == '\0') return(NULL);
- XX p1 = str;
- XX while (more) {
- XX switch (*p1) {
- XX case '0':
- XX case '1':
- XX case '2':
- XX case '3':
- XX case '4':
- XX case '5':
- XX case '6':
- XX case '7':
- XX case '8':
- XX case '9':
- XX val = val * 10 + (*p1 - '0');
- XX break;
-
- XX case '-':
- XX *low = val;
- XX val = 0L;
- XX hyphen = TRUE;
- XX break;
-
- XX case ' ':
- XX case ',':
- XX case '\n':
- XX more = FALSE;
- XX retval = ++p1;
- XX *high = val;
- XX if (!hyphen) *low = val;
- XX break;
-
- XX case '\0':
- XX more = FALSE;
- XX retval = NULL;
- XX *high = val;
- XX if (!hyphen) *low = val;
- XX return (retval);
- XX }
- XX p1++;
- XX }
- XX return (retval);
- XX}
- @//E*O*F newscnt.c//
- chmod u=rw,g=rw,o=rw newscnt.c
-
- echo x - scanargs.c
- sed 's/^XX//' > "scanargs.c" <<'@//E*O*F scanargs.c//'
- XX/*
- XX * $Header: scanargs.c,v 1.3 83/05/22 02:21:32 thomas Exp $
- XX * Version 7 compatible
- XX * Argument scanner, scans argv style argument list.
- XX *
- XX * Some stuff is a kludge because sscanf screws up
- XX *
- XX * Gary Newman - 10/4/1979 - Ampex Corp.
- XX *
- XX * Modified by Spencer W. Thomas, Univ. of Utah, 5/81 to
- XX * add args introduced by a flag, add qscanargs call,
- XX * allow empty flags.
- XX *
- XX * Compiling with QUICK defined generates 'qscanargs' ==
- XX * scanargs w/o floating point support; avoids huge size
- XX * of scanf.
- XX *
- XX * If you make improvements we'd like to get them too.
- XX * Jay Lepreau lepreau@utah-20, decvax!harpo!utah-cs!lepreau
- XX * Spencer Thomas thomas@utah-20, decvax!harpo!utah-cs!thomas
- XX *
- XX * (I know the code is ugly, but it just grew, you see ...)
- XX *
- XX * Modified by: Spencer W. Thomas
- XX * Date: Feb 25 1983
- XX * 1. Fixed scanning of optional args. Now args introduced by a flag
- XX * must follow the flag which introduces them and precede any other
- XX * flag argument. It is still possible for a flag introduced
- XX * argument to be mistaken for a "bare" argument which occurs
- XX * earlier in the format string. This implies that flags may not
- XX * be conditional upon other flags, and a message will be generated
- XX * if this is attempted.
- XX *
- XX * 2. Usage message can be formatted by inserting newlines, tabs and
- XX * spaces into the format string. This is especially useful for
- XX * long argument lists.
- XX *
- XX * 3. Added n/N types for "numeric" args. These args are scanned
- XX * using the C language conventions - a number starting 0x is
- XX * hexadecimal, a number starting with 0 is octal, otherwise it is
- XX * decimal.
- XX */
-
- XX#include <stdio.h>
- XX#include <ctype.h>
- XX#include <varargs.h>
-
- XXtypedef char bool;
- XX/*
- XX * An explicit assumption is made in this code that all pointers look
- XX * alike, except possible char * pointers.
- XX */
- XXtypedef int *ptr;
-
- XX#define YES 1
- XX#define NO 0
- XX#define ERROR(msg) {fprintf(stderr, "msg\n"); goto error; }
-
- XX/*
- XX * Storage allocation macros
- XX */
- XX#define NEW( type, cnt ) (type *) malloc( (cnt) * sizeof( type ) )
- XX#define RENEW( type, ptr, cnt ) (type *) realloc( ptr, (cnt) * sizeof( type ) )
-
- XXstatic char * prformat();
- XXstatic bool isnum();
-
- XX/*
- XX * Argument list is (argc, argv, format, ... )
- XX */
- XX#ifndef QUICK
- XXscanargs ( va_alist )
- XX#else
- XXqscanargs ( va_alist )
- XX#endif
- XXva_dcl
- XX{
- XX int argc; /* Actual arguments */
- XX char **argv;
- XX char *format;
- XX va_list argl;
-
- XX register check; /* check counter to be sure all argvs
- XX are processed */
- XX register char *cp;
- XX register cnt;
- XX int optarg = 0; /* where optional args start */
- XX int nopt;
- XX char tmpflg, /* temp flag */
- XX typchr; /* type char from format string */
- XX char c;
- XX bool * arg_used; /* array of flags */
- XX char * malloc();
- XX ptr aptr; /* pointer to return loc */
-
- XX bool required;
- XX int excnt; /* which flag is set */
- XX bool exflag; /* when set, one of a set of exclusive
- XX flags is set */
-
- XX bool list_of; /* set if parsing off a list of args */
- XX bool comma_list; /* set if AT&T style multiple args */
- XX int * cnt_arg; /* where to stuff list count */
- XX int list_cnt; /* how many in list */
- XX /* These are used to build return lists */
- XX char ** strlist;
- XX int * intlist;
- XX long * longlist;
- XX float * fltlist;
- XX double *dbllist;
-
- XX char *ncp; /* remember cp during flag scanning */
- XX#ifndef QUICK
- XX char *cntrl; /* control string for scanf's */
- XX char junk[2]; /* junk buffer for scanf's */
-
- XX cntrl = "% %1s"; /* control string initialization for
- XX scanf's */
- XX#endif
-
- XX va_start( argl );
- XX argc = va_arg( argl, int );
- XX argv = va_arg( argl, char ** );
- XX format = va_arg( argl, char * );
-
- XX arg_used = NEW( bool, argc );
- XX if (arg_used == NULL)
- XX {
- XX fprintf(stderr, "malloc failed in scanargs, exiting\n");
- XX exit(-1);
- XX }
- XX else
- XX {
- XX for (cnt=0; cnt<argc; cnt++)
- XX arg_used[cnt] = NO;
- XX }
-
- XX check = 0;
- XX cp = format;
- XX /*
- XX * Skip program name
- XX */
- XX while ( *cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0' )
- XX cp++;
-
- XX while (*cp)
- XX {
- XX required = NO; /* reset per-arg flags */
- XX list_of = NO;
- XX comma_list = NO;
- XX list_cnt = 0;
- XX switch (*(cp++))
- XX {
- XX default: /* all other chars */
- XX break;
- XX case ' ': /* separators */
- XX case '\t':
- XX case '\n':
- XX optarg = 0; /* end of optional arg string */
- XX break;
-
- XX case '!': /* required argument */
- XX required = YES;
- XX case '%': /* not required argument */
- XXreswitch: /* after finding '*' or ',' */
- XX switch (typchr = *(cp++))
- XX {
- XX case ',': /* argument is AT&T list of things */
- XX comma_list = YES;
- XX case '*': /* argument is list of things */
- XX list_of = YES;
- XX list_cnt = 0; /* none yet */
- XX cnt_arg = va_arg( argl, int *); /* item count * here */
- XX goto reswitch; /* try again */
-
- XX case '$': /* "rest" of argument list */
- XX while ( argc > 1 && !arg_used[argc-1] )
- XX argc--; /* find last used argument */
- XX *va_arg( argl, int * ) = argc;
- XX break;
-
- XX case '-': /* argument is flag */
- XX if (optarg > 0)
- XX ERROR(Format error: flag conditional on flag not allowed);
-
- XX /* go back to label */
- XX ncp = cp-1; /* remember */
- XX cp -= 3;
- XX for (excnt = exflag = 0
- XX ; *cp != ' ' && !(*cp=='-' &&(cp[-1]=='!'||cp[-1]=='%'));
- XX (--cp, excnt++))
- XX {
- XX for (cnt = optarg+1; cnt < argc; cnt++)
- XX {
- XX /* flags all start with - */
- XX if (*argv[cnt] == '-' && !arg_used[cnt] &&
- XX !isdigit(argv[cnt][1]))
- XX if (*(argv[cnt] + 1) == *cp)
- XX {
- XX if (*(argv[cnt] + 2) != 0)
- XX ERROR (extra flags ignored);
- XX if (exflag)
- XX ERROR (more than one exclusive flag chosen);
- XX exflag++;
- XX required = NO;
- XX check += cnt;
- XX arg_used[cnt] = 1;
- XX nopt = cnt;
- XX *va_arg( argl, int *) |= (1 << excnt);
- XX break;
- XX }
- XX }
- XX }
- XX if (required)
- XX ERROR (flag argument missing);
- XX cp = ncp;
- XX /*
- XX * If none of these flags were found, skip any
- XX * optional arguments (in the varargs list, too).
- XX */
- XX if (!exflag)
- XX {
- XX va_arg( argl, int * ); /* skip the arg, too */
- XX while (*++cp && ! isspace(*cp))
- XX if (*cp == '!' || *cp == '%')
- XX {
- XX if ( *++cp == '*' || *cp == ',' )
- XX {
- XX cp++;
- XX va_arg( argl, int * );
- XX }
- XX /*
- XX * Assume that char * might be a
- XX * different size, but that all
- XX * other pointers are same size.
- XX */
- XX if ( *cp == 's' )
- XX va_arg( argl, char * );
- XX else
- XX va_arg( argl, ptr );
- XX }
- XX }
- XX else
- XX {
- XX optarg = nopt;
- XX cp++; /* skip over - */
- XX }
-
- XX break;
-
- XX case 's': /* char string */
- XX case 'd': /* decimal # */
- XX case 'o': /* octal # */
- XX case 'x': /* hexadecimal # */
- XX case 'n': /* "number" in C syntax */
- XX#ifndef QUICK
- XX case 'f': /* floating # */
- XX#endif
- XX case 'D': /* long decimal # */
- XX case 'O': /* long octal # */
- XX case 'X': /* long hexadecimal # */
- XX case 'N': /* long number in C syntax */
- XX#ifndef QUICK
- XX case 'F': /* double precision floating # */
- XX#endif
- XX for (cnt = optarg+1; cnt < argc; cnt++)
- XX {
- XX ncp = argv[cnt];
-
- XX if ( isnum( ncp, typchr, comma_list ) )
- XX {
- XX if ( typchr == 's' ) /* string? */
- XX continue; /* don't want numbers, then */
- XX }
- XX else if ( *ncp == '-' )
- XX if ( optarg > 0 ) /* end optional args? */
- XX {
- XX /* Eat the arg, too, if necessary */
- XX if ( list_cnt == 0 )
- XX if ( typchr == 's' )
- XX va_arg( argl, char * );
- XX else
- XX va_arg( argl, ptr );
- XX break;
- XX }
- XX else
- XX continue;
- XX else if ( typchr != 's' )
- XX continue; /* not number, keep looking */
- XX
- XX /*
- XX * Otherwise usable argument may already
- XX * be used. (Must check this after
- XX * checking for flag, though.)
- XX */
- XX if (arg_used[cnt]) continue;
-
- XX /*
- XX * If it's a comma-and-or-space-separated
- XX * list then count how many, and separate
- XX * the list into an array of strings.
- XX */
- XX if ( comma_list )
- XX {
- XX register char * s;
- XX int pass;
-
- XX /*
- XX * On pass 0, just count them. On
- XX * pass 1, null terminate each string
- XX */
- XX for ( pass = 0; pass <= 1; pass++ )
- XX {
- XX for ( s = ncp; *s != '\0'; )
- XX {
- XX if ( pass )
- XX strlist[list_cnt] = s;
- XX while ( (c = *s) != '\0' && c != ' ' &&
- XX c != '\t' && c != ',' )
- XX s++;
- XX if ( pass )
- XX *s = '\0';
-
- XX list_cnt++; /* count separators */
- XX /*
- XX * Two commas in a row give a null
- XX * string, but two spaces
- XX * don't. Also skip spaces
- XX * after a comma.
- XX */
- XX if ( c != '\0' )
- XX while ( *++s == ' ' || *s == '\t' )
- XX ;
- XX }
- XX if ( pass == 0 )
- XX {
- XX strlist = NEW( char *, list_cnt );
- XX list_cnt = 0;
- XX }
- XX }
- XX }
- XX else if ( list_of )
- XX list_cnt++; /* getting them one at a time */
- XX /*
- XX * If it's either type of list, then alloc
- XX * storage space for the returned values
- XX * (except that comma-separated string
- XX * lists already are done).
- XX */
- XX if ( list_of )
- XX {
- XX if ( list_cnt == 1 || comma_list )
- XX switch( typchr )
- XX {
- XX case 's':
- XX if ( !comma_list )
- XX strlist = NEW( char *, 1 );
- XX aptr = (ptr) &strlist[0];
- XX break;
- XX case 'n':
- XX case 'd':
- XX case 'o':
- XX case 'x':
- XX intlist = NEW( int, list_cnt );
- XX aptr = (ptr) &intlist[0];
- XX break;
- XX case 'N':
- XX case 'D':
- XX case 'O':
- XX case 'X':
- XX longlist = NEW( long, list_cnt );
- XX aptr = (ptr) &longlist[0];
- XX break;
- XX case 'f':
- XX fltlist = NEW( float, list_cnt );
- XX aptr = (ptr) &fltlist[0];
- XX break;
- XX case 'F':
- XX dbllist = NEW( double, list_cnt );
- XX aptr = (ptr) &dbllist[0];
- XX break;
- XX }
- XX else
- XX switch( typchr )
- XX {
- XX case 's':
- XX strlist = RENEW( char *, strlist,
- XX list_cnt );
- XX aptr = (ptr) &strlist[list_cnt-1];
- XX break;
- XX case 'n':
- XX case 'd':
- XX case 'o':
- XX case 'x':
- XX intlist = RENEW( int, intlist,
- XX list_cnt );
- XX aptr = (ptr) &intlist[list_cnt-1];
- XX break;
- XX case 'N':
- XX case 'D':
- XX case 'O':
- XX case 'X':
- XX longlist = RENEW( long, longlist,
- XX list_cnt );
- XX aptr = (ptr) &longlist[list_cnt-1];
- XX break;
- XX case 'f':
- XX fltlist = RENEW( float, fltlist,
- XX list_cnt );
- XX aptr = (ptr) &fltlist[list_cnt-1];
- XX break;
- XX case 'F':
- XX dbllist = RENEW( double, dbllist,
- XX list_cnt );
- XX aptr = (ptr) &dbllist[list_cnt-1];
- XX break;
- XX }
- XX }
- XX else
- XX aptr = va_arg( argl, ptr );
-
- XX if ( typchr == 's' )
- XX {
- XX if ( ! comma_list )
- XX *(char **)aptr = ncp;
- XX }
- XX else
- XX {
- XX nopt = 0;
- XX do {
- XX /*
- XX * Need to update aptr if parsing
- XX * a comma list
- XX */
- XX if ( comma_list && nopt > 0 )
- XX {
- XX ncp = strlist[nopt];
- XX switch( typchr )
- XX {
- XX case 'n':
- XX case 'd':
- XX case 'o':
- XX case 'x':
- XX aptr = (ptr) &intlist[nopt];
- XX break;
- XX case 'N':
- XX case 'D':
- XX case 'O':
- XX case 'X':
- XX aptr = (ptr) &longlist[nopt];
- XX break;
- XX case 'f':
- XX aptr = (ptr) &fltlist[nopt];
- XX break;
- XX case 'F':
- XX aptr = (ptr) &dbllist[nopt];
- XX break;
- XX }
- XX }
- XX /*
- XX * Do conversion for n and N types
- XX */
- XX tmpflg = typchr;
- XX if (typchr == 'n' || typchr == 'N' )
- XX if (*ncp != '0')
- XX tmpflg = 'd';
- XX else if (*(ncp+1) == 'x' ||
- XX *(ncp+1) == 'X')
- XX {
- XX tmpflg = 'x';
- XX ncp += 2;
- XX }
- XX else
- XX tmpflg = 'o';
- XX if (typchr == 'N')
- XX toupper( tmpflg );
-
-
- XX#ifndef QUICK
- XX cntrl[1] = tmpflg;/* put in conversion */
- XX if (sscanf (ncp, cntrl, aptr, junk) != 1)
- XX ERROR (Bad numeric argument);
- XX#else
- XX if (numcvt(ncp, tmpflg, aptr) != 1)
- XX ERROR (Bad numeric argument);
- XX#endif
- XX } while ( comma_list && ++nopt < list_cnt );
- XX }
- XX check += cnt;
- XX arg_used[cnt] = 1;
- XX required = NO;
- XX /*
- XX * If not looking for multiple args,
- XX * then done, otherwise, keep looking.
- XX */
- XX if ( !( list_of && !comma_list ) )
- XX break;
- XX else
- XX continue;
- XX }
- XX if (required)
- XX switch (typchr)
- XX {
- XX case 'x':
- XX case 'X':
- XX ERROR (missing hexadecimal argument);
- XX case 's':
- XX ERROR (missing string argument);
- XX case 'o':
- XX case 'O':
- XX ERROR (missing octal argument);
- XX case 'd':
- XX case 'D':
- XX ERROR (missing decimal argument);
- XX case 'f':
- XX case 'F':
- XX ERROR (missing floating argument);
- XX case 'n':
- XX case 'N':
- XX ERROR (missing numeric argument);
- XX }
- XX if ( list_cnt > 0 )
- XX {
- XX *cnt_arg = list_cnt;
- XX switch ( typchr )
- XX {
- XX case 's':
- XX *va_arg( argl, char *** ) = strlist;
- XX break;
- XX case 'n':
- XX case 'd':
- XX case 'o':
- XX case 'x':
- XX *va_arg( argl, int ** ) = intlist;
- XX break;
- XX case 'N':
- XX case 'D':
- XX case 'O':
- XX case 'X':
- XX *va_arg( argl, long ** ) = longlist;
- XX break;
- XX case 'f':
- XX *va_arg( argl, float ** ) = fltlist;
- XX break;
- XX case 'F':
- XX *va_arg( argl, double **) = dbllist;
- XX break;
- XX }
- XX if ( typchr != 's' )
- XX free( (char *) strlist );
- XX }
- XX else if ( cnt >= argc )
- XX {
- XX /* Fell off end looking, so must eat the arg */
- XX if ( typchr == 's' )
- XX va_arg( argl, char * );
- XX else
- XX va_arg( argl, ptr );
- XX }
- XX break;
- XX default: /* error */
- XX fprintf (stderr, "error in call to scanargs\n");
- XX return (0);
- XX }
- XX }
- XX }
-
- XX /* Count up empty flags */
- XX for (cnt=1; cnt<argc; cnt++)
- XX if (argv[cnt][0] == '-' && argv[cnt][1] == '-' && argv[cnt][2] == 0
- XX && !arg_used[cnt] )
- XX check += cnt;
-
- XX /* sum from 1 to N = n*(n+1)/2 used to count up checks */
- XX if (check != (((argc - 1) * argc) / 2))
- XX ERROR (extra arguments not processed);
-
- XX free(arg_used);
- XX return (1);
-
- XXerror:
- XX fprintf (stderr, "usage : ");
- XX if (*(cp = format) != ' ')
- XX {
- XX if ( *cp == '%' )
- XX {
- XX /*
- XX * This is bogus, but until everyone can agree on a name
- XX * for (rindex/strrchr) ....
- XX */
- XX for ( cp = argv[0]; *cp != '\0'; cp++ )
- XX ; /* find the end of the string */
- XX for ( ; cp > argv[0] && *cp != '/'; cp-- )
- XX ; /* find the last / */
- XX if ( *cp == '/' )
- XX cp++;
- XX fprintf( stderr, "%s", cp );
-
- XX cp = format + 1; /* reset to where it should be */
- XX }
- XX while (putc (*cp++, stderr) != ' ');
- XX }
- XX else
- XX fprintf (stderr, "?? ");
- XX while (*cp == ' ')
- XX cp++;
- XX prformat (cp, NO);
- XX free(arg_used);
- XX return 0;
- XX}
-
- XXstatic char *
- XXprformat (format, recurse)
- XXchar *format;
- XX{
- XX register char *cp;
- XX bool required, comma_list;
- XX int list_of;
-
- XX cp = format;
- XX if (recurse)
- XX putc (' ', stderr);
-
- XX required = NO;
- XX list_of = 0;
- XX comma_list = NO;
- XX while (*cp)
- XX {
- XX switch (*cp)
- XX {
- XX default:
- XX cp++;
- XX break;
- XX case ' ':
- XX case '\t':
- XX case '\n':
- XX putc(*cp, stderr);
- XX format = ++cp;
- XX break;
- XX case '!':
- XX required = YES;
- XX case '%':
- XXreswitch:
- XX switch (*++cp)
- XX {
- XX case ',':
- XX comma_list++;
- XX case '*':
- XX list_of++;
- XX goto reswitch;
-
- XX case '$': /* "rest" of argument list */
- XX if (!required)
- XX putc ('[', stderr);
- XX for (; format < cp - 1 - list_of; format++)
- XX putc (*format, stderr);
- XX fputs( " ...", stderr );
- XX if ( !required )
- XX putc( ']', stderr );
- XX break;
-
- XX case '-': /* flags */
- XX if (!required)
- XX putc ('[', stderr);
- XX putc ('-', stderr);
-
- XX if (cp - format > 2 + list_of)
- XX putc ('{', stderr);
- XX cp = format;
- XX while (*cp != '%' && *cp != '!')
- XX putc (*cp++, stderr);
- XX if (cp - format > 1 + list_of)
- XX putc ('}', stderr);
- XX cp += 2; /* skip !- or %- */
- XX if (*cp && !isspace(*cp))
- XX cp = prformat (cp, YES);
- XX /* this is a recursive call */
- XX if (!required)
- XX putc (']', stderr);
- XX break;
- XX case 's': /* char string */
- XX case 'd': /* decimal # */
- XX case 'o': /* octal # */
- XX case 'x': /* hexadecimal # */
- XX case 'f': /* floating # */
- XX case 'D': /* long decimal # */
- XX case 'O': /* long octal # */
- XX case 'X': /* long hexadecimal # */
- XX case 'F': /* double precision floating # */
- XX case 'n': /* numeric arg (C format) */
- XX case 'N': /* long numeric arg */
- XX if (!required)
- XX putc ('[', stderr);
- XX for (; format < cp - 1 - list_of; format++)
- XX putc (*format, stderr);
- XX if ( list_of != 0 )
- XX {
- XX if ( comma_list )
- XX putc( ',', stderr );
- XX else
- XX putc( ' ', stderr );
- XX fputs( "...", stderr );
- XX }
- XX if (!required)
- XX putc (']', stderr);
- XX break;
- XX default:
- XX break;
- XX }
- XX required = NO;
- XX list_of = NO;
- XX comma_list = NO;
- XX if (*cp) /* check for end of string */
- XX format = ++cp;
- XX if (*cp && !isspace(*cp))
- XX putc (' ', stderr);
- XX }
- XX if (recurse && isspace(*cp))
- XX break;
- XX }
- XX if (!recurse)
- XX putc ('\n', stderr);
- XX return (cp);
- XX}
-
- XX/*
- XX * isnum - determine whether a string MIGHT represent a number.
- XX * typchr indicates the type of argument we are looking for, and
- XX * determines the legal character set. If comma_list is YES, then
- XX * space and comma are also legal characters.
- XX */
- XXstatic bool
- XXisnum( str, typchr, comma_list )
- XXregister char * str;
- XXchar typchr;
- XXbool comma_list;
- XX{
- XX register char * allowed, * digits, * cp;
- XX bool hasdigit = NO;
-
- XX switch( typchr )
- XX {
- XX case 'n':
- XX case 'N':
- XX allowed = " \t,+-x0123456789abcdefABCDEF";
- XX break;
- XX case 'd':
- XX case 'D':
- XX allowed = " \t,+-0123456789";
- XX break;
- XX case 'o':
- XX case 'O':
- XX allowed = " \t,01234567";
- XX break;
- XX case 'x':
- XX case 'X':
- XX allowed = " \t,0123456789abcdefABCDEF";
- XX break;
- XX case 'f':
- XX case 'F':
- XX allowed = " \t,+-eE.0123456789";
- XX break;
- XX case 's': /* only throw out decimal numbers */
- XX default:
- XX allowed = " \t,+-.0123456789";
- XX break;
- XX }
- XX digits = allowed;
- XX while ( *digits != '0' )
- XX digits++;
- XX if ( ! comma_list )
- XX allowed += 3; /* then don't allow space, tab, comma */
-
- XX while ( *str != '\0' )
- XX {
- XX for ( cp = allowed; *cp != '\0' && *cp != *str; cp++ )
- XX ;
- XX if ( *cp == '\0' )
- XX return NO; /* if not in allowed chars, not number */
- XX if ( cp - digits >= 0 )
- XX hasdigit = YES;
- XX str++;
- XX }
- XX return hasdigit;
- XX}
-
- XX#ifdef QUICK
- XXnumcvt(str, conv, val)
- XXregister char *str;
- XXchar conv;
- XXint *val;
- XX{
- XX int base, neg = 0;
- XX register unsigned int d;
- XX long retval = 0;
- XX register char *digits;
- XX extern char *index();
- XX if (conv == 'o' || conv == 'O')
- XX base = 8;
- XX else if (conv == 'd' || conv == 'D')
- XX base = 10;
- XX else if (conv == 'x' || conv == 'X')
- XX base = 16;
- XX else
- XX return 0;
-
- XX if (*str == '-')
- XX {
- XX neg = 1;
- XX str++;
- XX }
- XX while (*str)
- XX {
- XX if (*str >= '0' && *str < '0'+base)
- XX d = *str - '0';
- XX else if (base == 16 && *str >= 'a' && *str <= 'f')
- XX d = 10 + *str - 'a';
- XX else if (base == 16 && *str >= 'A' && *str <= 'F')
- XX d = 10 + *str - 'A';
- XX else
- XX return 0;
- XX retval = retval*base + d;
- XX str++;
- XX }
- XX if (neg)
- XX retval = -retval;
- XX if (conv == 'D' || conv == 'O' || conv == 'X')
- XX *(long *) val = retval;
- XX else
- XX *val = (int) retval;
- XX return 1;
- XX}
- XX#endif QUICK
- @//E*O*F scanargs.c//
- chmod u=rw,g=rw,o=rw scanargs.c
-
- echo Inspecting for damage in transit...
- temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
- trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
- cat > $temp <<\!!!
- 29 244 1409 README
- 15 73 436 Makefile
- 96 593 3593 newscnt.1
- 437 1727 11200 newscnt.c
- 833 3103 19707 scanargs.c
- 1410 5740 36345 total
- !!!
- wc README Makefile newscnt.1 newscnt.c scanargs.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
- if test -s $dtemp
- then echo "Ouch [diff of wc output]:" ; cat $dtemp
- else echo "No problems found."
- fi
- exit 0
-